﻿using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

namespace SBPweb.RIA.Shared.Contracts.Events
{

    public class DynamicContextRequest
    {

        public DynamicContextRequest(object owner, string methodName, params object[] arguments)
        {
            List<Type> types = new List<Type>();

            this.Owner = owner;
            this.OwnerType = owner.GetType();
            this.ResultType = typeof(object);
            if (arguments != null)
            {
                foreach (object arg in arguments)
                {
                    DynamicContextRequest er = arg as DynamicContextRequest;
                    if (arg != null)
                    {
                        if (er != null)
                        {
                            types.Add(er.ResultType);
                        }
                        else
                        {
                            types.Add(arg.GetType());
                        }
                    }
                    else
                    {
                        types.Add(typeof(object));
                    }
                }
            }
            types.Add(typeof(object));

            this.Method = OwnerType.GetMethod(methodName, types.ToArray());

            if (this.Method == null)
            {
                throw new ArgumentOutOfRangeException("methodName");
            }

            this.Arguments = new List<KeyValuePair<Type, object>>();
            for (int i = 0; i < arguments.Length; i++)
            {
                this.Arguments.Add(new KeyValuePair<Type, object>(types[i], arguments[i]));
            }
        }

        internal object Owner
        {
            get;
            private set;
        }

        internal Type OwnerType
        {
            get;
            set;
        }

        internal MethodInfo Method
        {
            get;
            private set;
        }

        internal IList<KeyValuePair<Type, object>> Arguments
        {
            get;
            private set;
        }

        public virtual object Result
        {
            get;
            protected set;
        }

        public virtual Type ResultType
        {
            get;
            private set;
        }

        internal void EvaluateResult(DynamicContextRequestQueue context)
        {
            int i = 0;
            IList<object> args = new List<object>(Arguments.Count);
            foreach (KeyValuePair<Type, object> kvp in Arguments)
            {
                DynamicContextRequest er = kvp.Value as DynamicContextRequest;
                if (er != null && context.Results.ContainsKey(er))
                {
                    args.Add(context.Results[er]);
                }
                else
                {
                    args.Add(kvp.Value);
                }
                i++;
            }
            args.Add(context);
            Method.Invoke(Owner, args.ToArray());
        }

    }

    public class DynamicContextRequest<TResult> : DynamicContextRequest
    {

        public DynamicContextRequest(object owner, string methodName, params object[] arguments)
            : base(owner, methodName, arguments)
        {
        }

        public new TResult Result
        {
            get
            {
                return (TResult)base.Result;
            }
            protected set
            {
                base.Result = value;
            }
        }

        public override Type ResultType
        {
            get
            {
                return typeof(TResult);
            }
        }

    }

    public class DynamicContextRequest<TOwner, TResult> : DynamicContextRequest<TResult>
    {

        public DynamicContextRequest(TOwner owner, string methodName, params object[] arguments)
            : base(owner, methodName, arguments)
        {
            OwnerType = typeof(TOwner);
        }

    }

}